// Copyright 2021 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "base/android/jank_metric_uma_recorder.h"

#include <cstdint>

#include "base/android/jni_android.h"
#include "base/android/jni_array.h"
#include "base/android/jni_string.h"
#include "base/base_jni_headers/JankMetricUMARecorder_jni.h"
#include "base/metrics/histogram_functions.h"
#include "base/strings/strcat.h"
#include "base/time/time.h"
#include "base/trace_event/base_tracing.h"
#include "base/tracing_buildflags.h"

namespace base {
namespace android {

namespace {

void AddFrameToTrace(int64_t timestamp_ns, int64_t durations_ns) {
#if BUILDFLAG(ENABLE_BASE_TRACING)
  if (timestamp_ns < 0)
    return;
  auto t = perfetto::Track(static_cast<uint64_t>(timestamp_ns));
  TRACE_EVENT_BEGIN(
      "ui", "AndroidFrameVsync", t, [&](perfetto::EventContext ctx) {
        ctx.event()->set_timestamp_absolute_us(timestamp_ns / 1000);
      });
  TRACE_EVENT_END("ui", t, [&](perfetto::EventContext ctx) {
    ctx.event()->set_timestamp_absolute_us((timestamp_ns + durations_ns) /
                                           1000);
  });
#endif  // BUILDFLAG(ENABLE_BASE_TRACING)
}

}  // namespace

// This function is called from Java with JNI, it's declared in
// base/base_jni_headers/JankMetricUMARecorder_jni.h which is an autogenerated
// header. The actual implementation is in RecordJankMetrics for simpler
// testing.
void JNI_JankMetricUMARecorder_RecordJankMetrics(
    JNIEnv* env,
    const base::android::JavaParamRef<jstring>& java_scenario_name,
    const base::android::JavaParamRef<jlongArray>& java_timestamps_ns,
    const base::android::JavaParamRef<jlongArray>& java_durations_ns,
    const base::android::JavaParamRef<jlongArray>& java_jank_bursts_ns,
    jint java_missed_frames) {
  RecordJankMetrics(env, java_scenario_name, java_timestamps_ns,
                    java_durations_ns, java_jank_bursts_ns, java_missed_frames);
}

void RecordJankMetrics(
    JNIEnv* env,
    const base::android::JavaParamRef<jstring>& java_scenario_name,
    const base::android::JavaParamRef<jlongArray>& java_timestamps_ns,
    const base::android::JavaParamRef<jlongArray>& java_durations_ns,
    const base::android::JavaParamRef<jlongArray>& java_jank_bursts_ns,
    jint java_missed_frames) {
  std::string scenario_name = ConvertJavaStringToUTF8(env, java_scenario_name);
  std::vector<int64_t> timestamps_ns;
  std::vector<int64_t> durations_ns;
  std::vector<int64_t> jank_bursts_ns;

  JavaLongArrayToInt64Vector(env, java_timestamps_ns, &timestamps_ns);
  JavaLongArrayToInt64Vector(env, java_durations_ns, &durations_ns);
  JavaLongArrayToInt64Vector(env, java_jank_bursts_ns, &jank_bursts_ns);

  std::string frame_duration_histogram_name =
      base::StrCat({"Android.Jank.FrameDuration.", scenario_name});
  std::string jank_burst_histogram_name =
      base::StrCat({"Android.Jank.JankBursts.", scenario_name});
  std::string missed_frames_histogram_name =
      base::StrCat({"Android.Jank.MissedFrames.", scenario_name});

  for (size_t i = 0; i < timestamps_ns.size(); ++i) {
    AddFrameToTrace(timestamps_ns[i], durations_ns[i]);
  }

  for (const int64_t frame_duration_ns : durations_ns) {
    base::UmaHistogramTimes(frame_duration_histogram_name,
                            base::Nanoseconds(frame_duration_ns));
  }

  for (const int64_t jank_burst_duration_ns : jank_bursts_ns) {
    base::UmaHistogramTimes(jank_burst_histogram_name,
                            base::Nanoseconds(jank_burst_duration_ns));
  }

  base::UmaHistogramCounts1000(missed_frames_histogram_name,
                               java_missed_frames);
}

}  // namespace android
}  // namespace base